﻿using System;
using System.Net;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Documents;
using System.Windows.Ink;
using System.Windows.Input;
using System.Windows.Media;
using System.Windows.Media.Animation;
using System.Windows.Shapes;
using System.Collections.Generic;

namespace MapEngine
{
    public class HeightMap
    {
        bool NoiseInitialized = false;
        float[,] Noise;
        int MaxWidth;
        int MaxHeight;
        float Frequency;
        float Amplitude;
        float Persistance;
        int Octaves;
        int Seed;

        //Frequency of 0.0625, an Amplitude of 1.0, a Persistance of 0.5 and 16 Octaves 
        public HeightMap(int maxMapWidth, int maxMapHeight,
            float frequency, float amplitude, float persistance,
            int octaves, int seed)
        {
            MaxWidth = maxMapWidth;
            MaxHeight = maxMapHeight;
            Noise = new float[MaxWidth, MaxHeight];
            Frequency = frequency;
            Amplitude = amplitude;
            Persistance = persistance;
            Octaves = octaves;
            Seed = seed;
            GenerateNoise(Seed);
        }

        public List<List<float>> getMap(Point position, Point size, float maxHeight)
        {
            //GetRandomHeight((float)position.X, (float)position.Y, maxHeight);
            List<List<float>> map = new List<List<float>>();
            for (int i = 0; i != size.Y; i++)
            {
                List<float> row = new List<float>();
                map.Add(row);
                for (int j = 0; j != size.X; j++)
                {
                    //row.Add(Noise[j, i]);
                    row.Add(GetRandomHeight(j, i, maxHeight));
                }
            }
            return map;
        }

        //Gets the value for a specific X and Y coordinate 
        float GetRandomHeight(float X, float Y, float MaxHeight)
        {
            
            float FinalValue = 0.0f;
            for (int i = 0; i < Octaves; ++i)
            {
                FinalValue += GetSmoothNoise(X * Frequency, Y * Frequency) * Amplitude;
                Frequency *= 2.0f;
                Amplitude *= Persistance;
            }
            if (FinalValue < -1.0f)
            {
                FinalValue = -1.0f;
            }
            else if (FinalValue > 1.0f)
            {
                FinalValue = 1.0f;
            }
            return FinalValue * MaxHeight;
        } 

        //This function is a simple bilinear filtering function which is good enough. 
        //You can do cosine or bicubic if you really want though. 
        private float GetSmoothNoise(float X, float Y)
        {
            float FractionX = X - (int)X;
            float FractionY = Y - (int)Y;
            int X1 = ((int)X + MaxWidth) % MaxWidth;
            int Y1 = ((int)Y + MaxHeight) % MaxHeight; 

            //for cool art deco looking images, do +1 for X2 and Y2 instead of -1...
            int X2 = ((int)X + MaxWidth - 1) % MaxWidth;
            int Y2 = ((int)Y + MaxHeight - 1) % MaxHeight; 

            float FinalValue = 0.0f;
            FinalValue += FractionX * FractionY * Noise[X1, Y1];
            FinalValue += FractionX * (1-FractionY) * Noise[X1, Y2];
            FinalValue += (1-FractionX) * FractionY * Noise[X2, Y1];
            FinalValue += (1-FractionX) * (1-FractionY) * Noise[X2, Y2]; 

            return FinalValue;
        } 

        private void GenerateNoise(int Seed)
        {
            if (NoiseInitialized)                //A boolean variable in the class to make sure we only do this once
                return;
                //Create the noise table where MAX_WIDTH and MAX_HEIGHT are set to some value>0
            Random RandomGenerator = new Random(Seed); //Create the random generator (just using C#'s at the moment)
            for (int x = 0; x < MaxWidth; ++x)
            {
                for (int y = 0; y < MaxHeight; ++y)
                {
                    Noise[x,y] = ((float)(RandomGenerator.NextDouble()) - 0.5f) * 2.0f;  //Generate noise between -1 and 1
                }
            }
            NoiseInitialized = true;
        } 
    }
}
